// 
//  Copyright (C) 2009 Jacek Trubłajewicz
// 
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
// 
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//  GNU General Public License for more details.
//  
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// 

using System;
using System.Collections.Generic;
using System.Threading;
using System.Text;
using System.Text.RegularExpressions;
using Mono.Unix;
using NLog;

namespace wxGadu
{
	
	public class Chat
	{
		#region data members
		private ChatFrame window;
		private int callerUin;
		private string callerName;
		private ContactInfo callerData;
		private int userUin;
		private string userName;
		private Dispatcher dispatcher;
		private LimitedList<ChatMessage> messages;
		#endregion
		private MessageFormatter incomingFormatter;
		private MessageFormatter outgoingFormatter;
		private Mutex messagesMutex;
		private StringBuilder messageBuilder;
		private Regex messagesRegex;
		private string chatHtmlPattern;
#if LOG
		private Logger logger = LogManager.GetCurrentClassLogger();
#endif
		
		public Chat(int callerUin, Dispatcher owner)
		{
			this.callerUin = callerUin;
			this.dispatcher = owner;
			this.callerName = string.Empty;
			
			if (dispatcher.ContactMgr.Contacts.ContainsKey(callerUin))
				callerData = dispatcher.ContactMgr.Contacts[callerUin];
			else
				callerData = null;
			
			if (callerData != null)
				callerName = callerData.Name;
			
			userUin = Int32.Parse(dispatcher.AppCfg.UserConf.Uin);
			userName = dispatcher.AppCfg.UserConf.Name;
			
			incomingFormatter = new MessageFormatter(FormatterPaterns.INCOMING_PATTERN);
			outgoingFormatter = new MessageFormatter(FormatterPaterns.OUTGOING_PATTERN);
			messageBuilder = new StringBuilder();
			messagesMutex = new Mutex();
			messages = new LimitedList<ChatMessage>(dispatcher.AppCfg.UserConf.ChatWindowMessagesCount);
			
			chatHtmlPattern = "<html>" +
				"<head>" +
				"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />" +
				"</head>" +
				"<body>"  +
					"<table border=\"0\" cellpadding=\"5\" cellspacing=\"0\" width=\"100%\" height=\"100%\">" +
					"#{messages}" +
					"</table>" +
				"</body>" +
				"</html>";
			messagesRegex = new Regex("#{messages}");
		}
		
		/// <summary>
		/// Destructor
		/// </summary>
		~Chat()
		{
			// save chat history if user want to
			dispatcher.HistoryMgr.DumpChatToFile(callerUin);
		}
		
		/// <summary>
		/// Adds incoming (received) message into messages list and 
		/// render messages into chat window
		/// </summary>
		/// <param name="time">
		/// A <see cref="System.DateTime"/>
		/// Time stamp of message
		/// </param>
		/// <param name="message">
		/// A <see cref="System.String"/>
		/// Preformatted message body <see cref="Dispatcher.PreformatIncomingMessage"/>
		/// </param>
		public void AddIncomingMessage(System.DateTime time, string message)
		{
			messagesMutex.WaitOne();
			{
				ChatMessage newMsg = new ChatMessage(callerUin, callerName, time, message, incomingFormatter);
				messages.Add(newMsg);
				// now render all messages into chat window (if exists)
				RenderMessages();
				// add to message to history
				if (dispatcher.AppCfg.UserConf.SaveHistory)
					dispatcher.HistoryMgr.AddChatMessage(callerUin, newMsg);
			}
			messagesMutex.ReleaseMutex();
		}
		
		/// <summary>
		/// Adds outgoing (sent) message into messages list and 
		/// render messages into chat window
		/// </summary>
		/// <param name="time">
		/// A <see cref="System.DateTime"/>
		/// Time stamp of message
		/// </param>
		/// <param name="message">
		/// A <see cref="System.String"/>
		/// Preformatted message body <see cref="PreformatOutgoingMessage"/>
		/// </param>
		public void AddOutgoingMessage(System.DateTime time, string message)
		{
			messagesMutex.WaitOne();
			{
				ChatMessage newMsg = new ChatMessage(userUin, userName, time, message, outgoingFormatter);
				messages.Add(newMsg);
				// now render all messages into chat window (if exists)
				RenderMessages();
				// add to message to history
				if (dispatcher.AppCfg.UserConf.SaveHistory)
					dispatcher.HistoryMgr.AddChatMessage(callerUin, newMsg);
			}
			messagesMutex.ReleaseMutex();
		}
		
		public void ClearMessageList()
		{
			messagesMutex.WaitOne();
			{
				// clear list 
				messages.Clear();
				//clear window
				window.EventHandler.AddPendingEvent(new wx.UpdateUIEvent((int) FakeId.clearChatWindow));
			}
			messagesMutex.ReleaseMutex();
		}
		
		private void RenderMessages()
		{	
//			messagesMutex.WaitOne();
//			{
#if LOG
			logger.Trace(String.Format(Catalog.GetString("Prepearing messages to render: {0} {1}"), callerName, callerUin));
#endif
			messageBuilder.Remove(0, messageBuilder.Length);
			// foreach on all messages on the list
			// create html version of each message
			// display messages onto chat window
			// if chat window exists
#if LOG
			logger.Trace(String.Format(Catalog.GetString("There is: {0} messages in UIN: {1} chat messages list"), messages.Count, callerUin));
#endif
			for (int i = messages.Count - 1; i >= 0; i--)
			{
					messageBuilder.Append(messages[i].FormattedMessage);
			}
			
			if (window != null)
			{
#if LOG
				logger.Trace(String.Format(Catalog.GetString("Rendering messages into window: {0} {1}"), callerName, callerUin));
#endif
				window.MessagesHtmlCode = messagesRegex.Replace(chatHtmlPattern, messageBuilder.ToString(), 1);

			}
			else
			{
#if LOG
				logger.Trace(String.Format(Catalog.GetString("Chat window not created yet, rendering skipped: {0} {1}"), callerName, callerUin));
#endif
			}
//			}
//			messagesMutex.ReleaseMutex();
		}
		
		//// <value>
		/// Chat window connected to this <c>Chat</c> object
		/// </value>
		/// <remarks>
		/// After successful set, <c>Chat.RenderMessages()</c>
		/// is called automaticaly if <c>value</c> is not null.
		/// </remarks>
		public ChatFrame ChatWindow
		{
//			set 
//			{
//				if (window == null)
//				{
//					window = value;
//					RenderMessages();
//				}
//				else
//				{
//#if LOG
//					logger.Warn(Catalog.GetString("Attempt to set new chat window, but window is already set"));
//#endif
//				}
//			}
			set 
			{ 
				window = value; 
				if (window != null)
					RenderMessages();
			}			
			get { return window; }
		}
		
		public int CallerUin
		{
			get { return callerUin; }
		}
		
		public string CallerName
		{
			get { return callerName; }
		}
	}
}
